package org.br.plotter.gui;

import javax.imageio.ImageIO;
import javax.swing.*;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;

import java.awt.BasicStroke;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Paint;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import org.br.plotter.functions.transformer.*;
import org.br.plotter.functions.*;
import org.br.plotter.parser.ParseException;
import org.br.plotter.parser.fft.FFT;
import org.br.plotter.parser.fft.FastFourierTransform;

public class FramePlotter extends JFrame implements ActionListener,
		ListSelectionListener {
	private int[] xs;
	private int[] ys;
	FastFourierTransform transformer = new FastFourierTransform();
	private LabelComponentPanel yEquation = new LabelComponentPanel("y(x)=", "");
	private LabelComponentPanel equation = new LabelComponentPanel("x(t)=", "");
	private JScrollPane scrolle = new JScrollPane();
	private JList embededFunctions = new JList(FunctionLocator.getFunctions()
			.toArray());
	JPanel contentLayOut = new JPanel();
	JPanel drawingLayOut = new JPanel(new CardLayout());
	JPanel configuration = new JPanel();
	JPanel equationsPanel = new JPanel();
	JPanel equations = new JPanel();
	ButtonGroup radioGroup = new ButtonGroup();
	JPanel radioButtons = new JPanel();
	JRadioButton xyRadio = new JRadioButton("Default Equation");
	JRadioButton polar = new JRadioButton("Polarized Equation");
	JRadioButton thirdeParty = new JRadioButton("Bi equation ");
	JButton plotter = new JButton("Plot");
	private JMenu menu = new JMenu("External Operation");
	private int scaleX;
	private int scaleY;
	private double[] ranges;
	List<double[]> data;

	public FramePlotter() {
		setTitle("Java Function Plotter Frame");
		FunctionLocator.registerHandler(new GUIExceptionHandler());
		scrolle.setViewportView(embededFunctions);
		contentLayOut.add(drawingLayOut);
		contentLayOut.add(configuration);
		contentLayOut.setLayout(new EqualComponentLayoutManager(true, 25));
		setContentPane(contentLayOut);
		radioButtons.setLayout(new BoxLayout(radioButtons, BoxLayout.Y_AXIS));
		JRadioButton[] radios = { xyRadio, polar, thirdeParty };
		configureRadioButtons(radios);
		equationsPanel
				.setLayout(new BoxLayout(equationsPanel, BoxLayout.Y_AXIS));
		equationsPanel.add(yEquation);
		JTextField f = new JTextField();
		f.setSize(200, 20);

		equationsPanel.add(plotter);
		embededFunctions.addListSelectionListener(this);
		configuration.add(equationsPanel);

		configuration.add(radioButtons);
		configuration.add(scrolle);
		xyRadio.setSelected(true);
		plotter.addActionListener(this);
		drawingLayOut.setBackground(Color.WHITE);
		drawingLayOut.setBorder(BorderFactory.createLineBorder(Color.black));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		JMenu fileMenu = new JMenu("File");
		JMenuItem save = new JMenuItem("Save");
		fileMenu.add(save);
		menuBar.add(fileMenu);
		save.addActionListener(new ActionListener(){

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(data == null)
					return;
				JFileChooser saver = new JFileChooser();
				saver.setFileFilter(new FileFilter(){

					@Override
					public boolean accept(File file) {
						// TODO Auto-generated method stub
						return file.isDirectory() ||  file.getName().endsWith(".jpg");
					}

					@Override
					public String getDescription() {
						// TODO Auto-generated method stub
						return "JPG Image";
					}});
				if(saver.showSaveDialog(FramePlotter.this) == JFileChooser.APPROVE_OPTION) {
					File selectedFile = saver.getSelectedFile();
					if(!selectedFile.getName().endsWith(".jpg")) {
						String path = selectedFile.getAbsolutePath() + ".jpg";
						selectedFile = new File(path);
					}
					BufferedImage image = new BufferedImage( drawingLayOut.getWidth(),drawingLayOut.getHeight(),BufferedImage.TYPE_INT_RGB);
					Graphics2D g= image.createGraphics();
					render(g);
				
					try {
						ImageIO.write(image, "jpg", selectedFile);
					} catch (IOException e) {
						FunctionLocator.getExceptionHandler().handleException(e);
					}									
				}				
			}						
		});
		JMenu view = new JMenu("View");
		menuBar.add(view);
		JMenuItem changeScale = new JMenuItem("Change Scale");
		changeScale.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (data != null) {
					render((Graphics2D) drawingLayOut.getGraphics(), data);
				}
			}
		});
		view.add(changeScale);
//		menuBar.add(menu);
		JMenuItem item = new JMenuItem("FFT");
		item.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (data != null) {
					data = transformer.fft(data);
					render((Graphics2D) drawingLayOut.getGraphics(), data);

				}
			}
		});
		menu.add(item);
		
		JMenuItem inversion = new JMenuItem("Inversion FFT");
		inversion.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (data != null) {
					data = FFT.ifft(data);
					render((Graphics2D) drawingLayOut.getGraphics(), data);

				}
			}
		});
		menu.add(inversion);
	}

	private void configureRadioButtons(JRadioButton[] data) {
		for (JRadioButton radio : data) {
			radioGroup.add(radio);
			radio.addActionListener(this);
			radioButtons.add(radio);
		}
	}

	public void paint(Graphics g1) {
		// TODO Auto-generated method stub
		super.paint(g1);
		if (ranges != null && xs != null) {
			Graphics2D g = (Graphics2D) drawingLayOut.getGraphics();
			render(g);
		}
	}

	private void render(Graphics2D g) {
				g.drawLine((int) ranges[2], 0, (int) ranges[2], drawingLayOut
				.getHeight());
		g.drawLine(0, (int) ranges[3], drawingLayOut.getWidth(),
				(int) ranges[3]);
		int width = drawingLayOut.getWidth();
		int offsetX = (int)(width - ranges[2]);
		int stepXCount = (int)(offsetX/ranges[0]);
		int stepXStart = width - (int)ranges[2]- (int)(stepXCount * ranges[0]);
		for(int i=stepXStart;i<width;i+= ranges[0]) {
			g.drawLine(i, (int)ranges[3], i, (int)ranges[3] - 10);
			char[] chr=String.valueOf((i - width + ranges[2])/ranges[0]).toCharArray();
			g.drawChars(chr, 0, chr.length, i-10, (int)ranges[3] + 15);
		}
		int height = drawingLayOut.getHeight();
		int offset = (int)(height - ranges[3]);
		int stepCount = (int)(offset/ranges[1]);
		int stepStart = height - (int)ranges[3] - (int)(stepCount*ranges[1]);
		
		for(int i=stepStart;i<height;i+= ranges[1]) {
			g.drawLine((int)ranges[2], i, (int)ranges[2] +10, i);
			char[] chr=String.valueOf(( height-i - ranges[3])/ranges[1]).toCharArray();
			g.drawChars(chr, 0, chr.length, (int)ranges[2]-10,i );
		}
		g.drawPolyline(xs, ys, ys.length);
	}

	public void actionPerformed(ActionEvent event) {
		Object source = event.getSource();
		if (source == plotter) {
			try {
				render((Graphics2D) drawingLayOut.getGraphics(), drawingLayOut
						.getWidth(), drawingLayOut.getHeight());
			} catch (ParseException e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(this, e.getLocalizedMessage(),
						"Error", JOptionPane.ERROR_MESSAGE);
			}
		} else if (source == xyRadio) {
			removeYEquation(equationsPanel, yEquation, equation, "y=f(x)");
		} else if (source == polar) {
			removeYEquation(equationsPanel, yEquation, equation, "r=f(t)");
		} else if (source == thirdeParty) {
			addInOrder(equationsPanel, equation, yEquation, "x=f(t)", "y=f(t)");
		}
	}

	private void addInOrder(JPanel equationsPanel,
			LabelComponentPanel equation, LabelComponentPanel yEquation,
			String xlabel, String ylabel) {
		removeYEquation(equationsPanel, yEquation, equation, ylabel);
		removeYEquation(equationsPanel, equation, yEquation, xlabel);
		equationsPanel.add(equation);
		equationsPanel.add(yEquation);
		equationsPanel.revalidate();
	}

	private void removeYEquation(JPanel equationsPanel,
			LabelComponentPanel yEquation, LabelComponentPanel equation,
			String label) {
		Component[] allComponents = equationsPanel.getComponents();
		for (Component component : allComponents) {
			if (component == equation) {
				equationsPanel.remove(component);
			}
		}
		yEquation.setLabel(label);
		equationsPanel.revalidate();
	}

	protected void render(Graphics2D g, int w, int h) throws ParseException {
		if (xyRadio.isSelected()) {
			renderDefault(g, w, h);
		} else if (polar.isSelected()) {
			renderPolar(g, w, h);
		} else if (thirdeParty.isSelected()) {
			renderBiEquation(g, w, h);
		}

	}

	protected void renderDefault(Graphics2D g, int w, int h)
			throws ParseException {
		ArrayTransformer defaulrTransformer = ArrayTransformer.DefaultTransformer;
		String[] labels = { "Minimum X", "Maximum X", "Number Of Points" };
		double[] initialData = { 0, w, h };
		XYScalePanel scalePanel = new XYScalePanel(labels, initialData);

		CustomMessage dialog = new CustomMessage(scalePanel);
		double[] d = scalePanel.getRequiredData();
		data = defaulrTransformer.getRangeArray(d[0], d[1], (int) d[2],
				yEquation.getValue());
		render(g, data, w, h);

	}

	protected void renderPolar(Graphics2D g, int w, int h)
			throws ParseException {
		ArrayTransformer defaulrTransformer = ArrayTransformer.PolarTransformer;
		String[] labels = { "Minimom ratial as degree",
				"Maximum ratial as degree", "Number Of Points" };
		double[] initialData = { 0, 360, 360 };
		XYScalePanel scalePanel = new XYScalePanel(labels, initialData);
		CustomMessage dialog = new CustomMessage(scalePanel);
		double[] d = scalePanel.getRequiredData();
		data = defaulrTransformer.getRangeArray(Math.toRadians(d[0]), Math
				.toRadians(d[1]), (int) d[2], yEquation.getValue());
		render(g, data, w, h);

	}

	protected void renderBiEquation(Graphics2D g, int w, int h)
			throws ParseException {
		String[] labels = { "Minimum t", "Maximum t", "Number Of Points" };
		double[] initialData = { 0, w, h };

		XYScalePanel scalePanel = new XYScalePanel(labels, initialData);
		CustomMessage dialog = new CustomMessage(scalePanel);
		double[] d = scalePanel.getRequiredData();
		data = ArrayTransformer.getRangeArray(d[0], d[1], (int) d[2], equation
				.getValue(), yEquation.getValue());
		render(g, data, w, h);

	}

	protected void render(Graphics2D g, List<double[]> points, int w, int h) {
		ranges = ArrayTransformer.calculateDefaultScale(points, w, h);
		render(g, points);
	}

	private void render(Graphics2D g, List<double[]> points) {
		String[] labels = { "Range X", "Range Y", "Central X0", "Central Y0" };
		XYScalePanel scalePanel = new XYScalePanel(labels, ranges);
		CustomMessage message = new CustomMessage(scalePanel);
		ranges = scalePanel.getRequiredData();
		
		int[][] xy = ArrayTransformer.transformByScale(points, ranges);
		xs = xy[0];
		ys = xy[1];
		
		repaint();
	}

	private Function getDefaultEquation() {
		return getFunction(equation);
	}

	private Function[] getBiEquations() {
		Function[] result = { getFunction(equation), getFunction(yEquation) };
		return result;
	}

	private Function getFunction(LabelComponentPanel field) {
		String equation = field.getValue();
		Function func = null;
		try {
			func = Function.parseFunction(equation);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			FunctionLocator.getExceptionHandler().handleException(e);
			return null;
		}
		return func;
	}

	public static void main(String[] args) {
		FramePlotter frame = new FramePlotter();
		frame.setLocationRelativeTo(null);
		frame.setSize(800, 600);
		frame.setVisible(true);
	}

	public void valueChanged(ListSelectionEvent arg0) {
		Object obj = embededFunctions.getSelectedValue();
		if (obj != null)
			equation.setValue((String) obj);
	}

}